using System;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;

namespace OpenWaves
{
    public class FileLoggingService : ILoggingService, IDisposable
    {
        private readonly object synchRoot = new object();
        private readonly string logDirectory;
        private readonly LogEntrySeverity logSeverityThreshold;

        private TextWriter logFile;

        public FileLoggingService() : this(ServiceLocator.Resolve<IFileLoggingServiceConfiguration>())
        {
        }

        public FileLoggingService(IFileLoggingServiceConfiguration configuration) : this(configuration.LogDirectory, configuration.LogSeverityThreshold)
        {            
        }

        public FileLoggingService(string logDirectory) : this(logDirectory, LogEntrySeverity.Debug)
        {
        }

        public FileLoggingService(string logDirectory, LogEntrySeverity logSeverityThreshold)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(logDirectory) == false);

            this.logDirectory = logDirectory;
            this.logSeverityThreshold = logSeverityThreshold;
        }

        public void Log(LogEntrySeverity severity, Exception exception, string message)
        {
            if (this.ShouldLog(severity) == false)
                return;

            lock (this.synchRoot)
            {
                this.EnsureLogFile();

                this.logFile.WriteLine("{0} {1} {2}: {3}", Time.Now.ToShortDateString(), Time.Now.ToLongTimeString(), severity, message);
                if (exception != null)
                    this.logFile.WriteLine(exception.ToString());

                this.logFile.WriteLine();
                this.logFile.Flush();
            }
        }

        public void Log(LogEntrySeverity severity, string message)
        {
            if (this.ShouldLog(severity) == false)
                return;

            lock (this.synchRoot)
            {
                this.EnsureLogFile();

                this.logFile.WriteLine("{0} {1} {2}: {3}", Time.Now.ToShortDateString(), Time.Now.ToLongTimeString(), severity, message);
                this.logFile.Flush();
            }
        }

        private bool ShouldLog(LogEntrySeverity severity)
        {
            return severity >= this.logSeverityThreshold;
        }

        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
        private void EnsureLogFile()
        {
            if (this.logFile != null)
                return;

            if (Directory.Exists(this.logDirectory) == false)
                Directory.CreateDirectory(this.logDirectory);


            string fileName = Time.Now.ToString("yyyy.MM.dd HH.mm ", CultureInfo.InvariantCulture) + GetApplicationName() + ".log";
            string filePath = Path.Combine(this.logDirectory, fileName);

            //allow others to read it
            this.logFile = new StreamWriter(File.Open(filePath, FileMode.Append, FileAccess.Write, FileShare.Read));
            this.logFile.WriteLine("=================================================================================================");
            this.logFile.WriteLine("This log file has been created by OpenWaves.FileLoggingService.");
            this.logFile.WriteLine("Please be aware that the service is only intended for development and testing purposes!");
            this.logFile.WriteLine("Use Log4Net or another well tested logging library as a basis for implementing ILoggingService.");
            this.logFile.WriteLine("=================================================================================================");
        }

        private static string GetApplicationName()
        {
            if (System.Reflection.Assembly.GetEntryAssembly() != null)
            {
                return System.Reflection.Assembly.GetEntryAssembly().GetName().Name;
            }
            else
            {
                return Path.GetFileNameWithoutExtension(AppDomain.CurrentDomain.FriendlyName);
            }
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.logFile.Dispose();
            }
        }
    }
}